import { AnyMongoAbility, InferSubjects } from '@casl/ability';
import { SetMetadata } from '@nestjs/common';
import { Action } from 'src/enum/action.enum';

export enum CHECK_POLICIES_KEY {
  HANDLER = 'CHECK_POLICIES_HANDLER',
  CAN = 'CHECK_POLICIES_CAN',
  CANNOT = 'CHECK_POLICIES_CANNOT',
}

export type CaslHandlerType = PolicyHandlerCallback | PolicyHandlerCallback[];

/* 
实现的效果
用户访问一个 guards  
服务端读取 routes上的 meta 信息
meta 信息上会有三个注解 @CheckPolicies @Can @Cannot
如果是 @CheckPolicies 用户会传递一个函数  函数接收一个ability 返回值是布尔值
@Can / @Cannot  接收三个参数 Action  Subject  Conditions
*/
export type PolicyHandlerCallback = (ability: AnyMongoAbility) => boolean;

// @CheckPolicies -> handler -> ability => boolean
export const CheckPolicies = (...handlers: PolicyHandlerCallback[]) =>
    SetMetadata(CHECK_POLICIES_KEY.HANDLER, handlers);

// Can -> Action , Subject , Conditions
export const Can = (
    action: Action,
    subject: InferSubjects<any>,
    conditions?: any,
) =>
    SetMetadata(CHECK_POLICIES_KEY.CAN, (ability: AnyMongoAbility) =>
        ability.can(action, subject, conditions),
    );

export const Cannot = (
    action: Action,
    subject: InferSubjects<any>,
    conditions?: any,
) =>
    SetMetadata(CHECK_POLICIES_KEY.CANNOT, (ability: AnyMongoAbility) =>
        ability.cannot(action, subject, conditions),
    );
